package com.lt.flashcard.model.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.bson.types.ObjectId;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.Query;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.zkoss.xel.fn.StringFns;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Messagebox;

import com.google.code.morphia.dao.BasicDAO;
import com.lt.flashcard.Utility;
import com.lt.flashcard.model.entity.FCSComment;
import com.lt.flashcard.model.entity.Flashcard;
import com.lt.flashcard.model.entity.FlashcardSet;
import com.lt.flashcard.model.entity.User;
import com.lt.flashcard.model.entity.Vote;

/**
 * Provide utility to interact with flashcardset service
 * 
 * @author ocsen
 * 
 */
public class FlashcardSetService extends com.lt.flashcard.model.service.BasicDAO<FlashcardSet> {

    private static final Logger log = Logger.getLogger(FlashcardSetService.class);

    public FlashcardSetService() {
        super(FlashcardSet.class);
    }

    private com.google.code.morphia.dao.BasicDAO<FCSComment, ObjectId> _fcscommentdao = null;

    /**
     * Get DAO to interact with comment
     * 
     * @return
     */
    private com.google.code.morphia.dao.BasicDAO<FCSComment, ObjectId> getFCSCommentDAO() {
        if (_fcscommentdao == null) {
            _fcscommentdao = new com.google.code.morphia.dao.BasicDAO<FCSComment, ObjectId>(FCSComment.class, Repository.getInstance()
                    .getDatastore());
        }
        return _fcscommentdao;
    }

    /**
     * Get list of related flashcardset
     * 
     * @param flashcardset
     * @return
     */
    public List<FlashcardSet> getRelatedFlashcardSet(FlashcardSet flashcardset) {
        if (flashcardset == null) {
            return null;
        }
        if (flashcardset.getTags().size() == 0) {
            return null;
        }

        Criteria crit = createCriteria();
        /*
         * Conjunction conj = Restrictions.conjunction(); for(Tag tag : flashcardset.getTags())
         * conj.add(Restrictions.eq("tags", tag)); crit.add(conj);
         */

        return crit.list();
    }

    /**
     * Update
     * 
     * @param flashcardset
     */
    public void resetRelatedFlashcardSet(FlashcardSet flashcardset) {

    }

    /**
     * Get list of original flashcardset of the flashcardset
     * 
     * @param flashcardset
     * @return
     */
    public List<FlashcardSet> getOriginalFlashcardSet(FlashcardSet flashcardset) {
        String listIds = flashcardset.getOriginalflashcardsetCache();
        if (StringUtils.isBlank(listIds)) {
            return new ArrayList<FlashcardSet>();
        }
        String[] arr = StringFns.split(listIds.trim(), ",");
        Long[] arrId = Utility.parseLong(arr);
        for (Long i : arrId) {
            log.info("Long i : " + i);
            FlashcardSet fcs = findByID(i);
            if (fcs == null) {
                arrId = org.apache.commons.lang3.ArrayUtils.removeElement(arrId, i);
            }
        }
        if (arrId.length > 0) {
            Criteria crit = createCriteria();
            crit.add(Restrictions.in("id", arrId));
            return crit.list();
        } else {
            return new ArrayList<FlashcardSet>();
        }
    }

    /**
     * Return list of users that allow to view the flashcardset
     * 
     * @param flashcardset
     * @return
     */
    public List<User> getAllowedUsers(FlashcardSet flashcardset) {
        Long[] arrIds = getAllowedUserIds(flashcardset);
        if (arrIds.length == 0) {
            return new ArrayList<User>();
        }

        Criteria crit = getCurrentSession().createCriteria(User.class);
        crit.add(Restrictions.in("id", arrIds));
        return crit.list();
    }

    /**
     * Return list of user ids that allowed to view the flashcardset
     * 
     * @param flashcardset
     * @return
     */
    public Long[] getAllowedUserIds(FlashcardSet flashcardset) {
        String ids = flashcardset.getAllowedusersCache();
        return Utility.parseLong(StringFns.split(ids, ","));
    }

    /**
     * Return list of users that not allowed to view the flashcardset
     * 
     * @param flashcardset
     * @return
     */
    public List<User> getDeniedUsers(FlashcardSet flashcardset) {
        Long[] arrIds = getDeniedUserIds(flashcardset);
        if (arrIds.length == 0) {
            return new ArrayList<User>();
        }
        Criteria crit = getCurrentSession().createCriteria(User.class);
        crit.add(Restrictions.in("id", arrIds));
        return crit.list();
    }

    /**
     * Return list of user ids that not allowed to view the flashcardset
     * 
     * @param flashcardset
     * @return
     */
    public Long[] getDeniedUserIds(FlashcardSet flashcardset) {
        String ids = flashcardset.getDeniedusersCache();
        return Utility.parseLong(StringFns.split(ids, ","));
    }

    /**
     * Re-calculate the original flashcardset and store to cache
     * 
     * @param flashcardset
     */
    public void resetOriginalFlashcardSet(FlashcardSet flashcardset) {
        Query query = getCurrentSession().createQuery("select fc.oriflashcardset.id from Flashcard fc where fc.flashcardSet = :fcs");
        query.setParameter("fcs", flashcardset);
        List lst = query.list();
        String result = StringFns.join(lst.toArray(), ",");
        flashcardset.setOriginalflashcardsetCache(result);
        save(flashcardset);
    }

    /**
     * Update the number of view for flashcardset
     */
    public void updateViewCount(FlashcardSet flashcardset) {
        AAService srv = Repository.getInstance().getAAService();
        Subject user = srv.getCurrentSubject();
        Session ses = user.getSession();
        Set<Long> ids = (Set<Long>) ses.getAttribute("viewed_fcs");
        boolean needUpdate = false;
        if (ids == null) {
            needUpdate = true;
            ids = new HashSet<Long>();
            ids.add(flashcardset.getId());
            ses.setAttribute("viewed_fcs", ids);
        } else if (!ids.contains(flashcardset.getId())) {
            needUpdate = true;
            ids.add(flashcardset.getId());
            ses.setAttribute("viewed_fcs", ids);
        }

        if (needUpdate) {
            flashcardset.setViewCount(flashcardset.getViewCount() + 1);
            save(flashcardset);
        }
    }

    /**
     * Update rating set
     */
    public void updateRating(FlashcardSet flashcardset, int currVote) {
        VoteService vsrv = Repository.getInstance().getVoteService();
        FlashcardSetService fsrv = Repository.getInstance().getFlashcardSetService();

        User user = Utility.getUser(); // TODO optimize this
        Vote vote = vsrv.getVote(user.getId(), flashcardset.getId(), 0);

        if (vote != null) {// neu da vote, vote lai voi gia tri moi
            if (vote.getRating() != currVote) {
                int voteTotal = flashcardset.getVoteTotal();
                double voteResult;
                if (voteTotal > 1) {
                    // calculate again voteResult before user hasn't just voted
                    voteResult = ((flashcardset.getVoteResult() * voteTotal) - vote.getRating()) / (voteTotal - 1);
                    // calculate and update voteResult
                    voteResult = ((voteResult * (voteTotal - 1)) + currVote) / (voteTotal);
                } else {
                    voteResult = currVote;
                    voteTotal = 0;
                }
                flashcardset.setVoteResult(voteResult);
                fsrv.save(flashcardset);
                // update vote mongo
                vote.setRating(currVote);
                vsrv.save(vote);
            }
        } else {
            int voteTotal = flashcardset.getVoteTotal();
            double voteResult = ((flashcardset.getVoteResult() * voteTotal) + currVote) / (voteTotal + 1);
            flashcardset.setVoteResult(voteResult);
            flashcardset.setVoteTotal(voteTotal + 1);
            fsrv.save(flashcardset);

            Vote nvote = new Vote();
            nvote.setFsid(flashcardset.getId());
            nvote.setRating(currVote);
            nvote.setUid(user.getId());
            vsrv.save(nvote);
        }
    }

    /**
     * get all flashcard set of current user
     * 
     * @param user
     *            The target user
     * @param excludeBuy
     *            exclude buy flashcardset
     */
    public List<FlashcardSet> getFlashcardsetsOfCurrUser(User user, boolean excludeBuy) {
        Criteria crit = getCurrentSession().createCriteria(FlashcardSet.class);
        crit.setFetchMode("flashcards", FetchMode.JOIN);
        crit.add(Restrictions.eq("author", user));
        if (excludeBuy) {
            crit.add(Restrictions.eq("admin", FlashcardSet.ADMIN_NO));
        }
        crit.addOrder(Order.asc("name"));
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        // If you want to fetch the distinct value of only one field, then you can go for this
        // crit.setProjection(Projections.distinct("propertyName"));

        return crit.list();
    }

    /**
     * save flashcards into flashcard set from another flashcard set and update blank flashcards
     */
    public void saveFlashcardsIntoNewFlashcardset(FlashcardSet nFlashcardset, FlashcardSet oFlashcardset) {
        FlashcardSetService fsSrv = Repository.getInstance().getFlashcardSetService();

        for (Flashcard fC : oFlashcardset.getFlashcards()) {
            Flashcard insertFlashcard = new Flashcard(fC);
            insertFlashcard.setOriflashcardset(oFlashcardset);
            nFlashcardset.getFlashcards().add(insertFlashcard);
        }

        updateBlankFlashcards(nFlashcardset.getFlashcards().size());

        fsSrv.save(nFlashcardset);
    }

    /**
     * update count of totalflashcars in flashcardset when add, delete flashcard
     */
    public void updateTotalFlashcards(FlashcardSet flashcardset) {
        flashcardset.setTotalflashcards(flashcardset.getFlashcards().size());
        save(flashcardset);
    }

    /**
     * update totalFlashcards for set exist
     */
    public void resetTotalFlashcards() {
        Query query = getCurrentSession().createQuery("select id from FlashcardSet");
        List<Long> ids = query.list();
        for (Long id : ids) {
            updateTotalFlashcards(findByID(id));
        }
    }

    /**
     * Return newly created flashcard of user
     * 
     * @param user
     * @param page
     *            Current page
     * @param pagesize
     *            The page size
     * @return
     */
    public List<FlashcardSet> getCreatedFlashcardsets(User user, int page, int pagesize) {
        Criteria crit = createCriteria();
        crit.add(Restrictions.eq("author", user));
        crit.addOrder(Order.desc("created"));
        crit.setMaxResults(pagesize);
        crit.setFirstResult(page * pagesize);
        return crit.list();
    }

    /**
     * Count the number of created flashcardsets
     * 
     * @param user
     * @return
     */
    public int countCreatedFlashcardsets(User user) {
        Criteria crit = createCriteria();
        crit.add(Restrictions.eq("author", user));
        crit.setProjection(Projections.rowCount());
        return (Integer) crit.uniqueResult();
    }

    /**
     * Get total number of flashcards that waiting for testing
     * 
     * @param flashcardset
     * @return
     */
    public int getNumOfWaitingWordForTest(FlashcardSet flashcardset) {
        int result = 0;
        Date now = new Date();
        String nowStr = Utility.formatDate(now);
        for (Flashcard fc : flashcardset.getFlashcards()) {
            if (fc.getLearnLevel() != Flashcard.LEARNLEVEL_NEW && fc.getLearnLevel() != Flashcard.LEARNLEVEL_FINISH) {
                if ((fc.getLearnNextdate() != null && fc.getLearnNextdate().before(now))
                        || (fc.getLasttestdate() != null && Utility.formatDate(fc.getLasttestdate()).equals(nowStr))) {
                    result++;
                }
            }
        }
        return result;
    }

    /*
     * overide func save basicDAO to fix error: A different object with same identifier was already associated with the
     * session
     */
    @Override
    public boolean save(FlashcardSet flashcardset) {
        if (flashcardset.getName().length() == 0 || flashcardset.getAuthor() == null) {
            return false;
        }

        flashcardset.setName(flashcardset.getName().trim());

        int totalnew = 0;
        if (flashcardset.getAdmin() == FlashcardSet.ADMIN_NO) {
            for (Flashcard fc : flashcardset.getFlashcards()) {
                if (fc.getId() == null) {
                    totalnew += 1;
                }
            }
        }

        org.hibernate.Session ses = getCurrentSession();

        User author = flashcardset.getAuthor();
        AAService aasrv = Repository.getInstance().getAAService();
        boolean isAdmin = aasrv.isAdmin(author);
        if (!isAdmin) {
            try {
                if (totalnew > author.getCurrentblankflashcard()) {
                    return false;
                }
            } catch (Exception e) {
                // TODO monitor to fix this, or remove
                log.error("Can't get current blank flashcard", e);
            }
        }

        if (flashcardset.getId() != null) {
            ses.update(flashcardset);
        } else {
            ses.saveOrUpdate(flashcardset);
            // add notification and activity
            // if current user is not admin, then add activity
            if (flashcardset.getAdmin() == FlashcardSet.ADMIN_NO && !isAdmin) {
                UserService usrv = Repository.getInstance().getUserService();
                usrv.addActivityCreateSet(author, flashcardset);
            }
        }

        if (totalnew > 0 && !isAdmin && flashcardset.getAdmin() == FlashcardSet.ADMIN_NO) {
            // update total flashcards of user
            try {
                int n = author.getCurrentblankflashcard() - totalnew;
                author.setCurrentblankflashcard(n > 0 ? n : 0);
                ses.update(author);
            } catch (NonUniqueObjectException e) {
                ses.merge(author);
                log.error("Catch non unique object exception");
            } catch (Exception e) {
                log.error("Can't save current blank flashcard", e);
            }
        }

        return true;
    }

    /*
     * check blank flashcard
     */
    public boolean enoughBlankFlashcard(int countInsertFlashcard) {
        User user = Utility.getUser();
        if (user != null) {
            if (user.getCurrentblankflashcard() >= countInsertFlashcard) {
                ;
            }
            return true;
        }
        return false;
    }

    /**
     * get counter checkbox is checked
     */
    @Deprecated
    public int countChecked(List<Checkbox> chkList) {
        int count = 0;
        for (Checkbox chk : chkList) {
            if (chk.isChecked()) {
                count++;
            }
        }
        return count;
    }

    /**
     * update blank Flashcards
     * 
     * @param count
     *            : flashcards are used
     */
    public void updateBlankFlashcards(int count) {
        UserService usrv = Repository.getInstance().getUserService();
        User user = Utility.getUser();
        user.setCurrentblankflashcard(user.getCurrentblankflashcard() - count);
        usrv.save(user);
    }

    /**
     * save flashcardset into new flashcardset and redirect to new flashcardset
     * 
     * @param flashcardset
     *            want to save
     */
    @Deprecated
    public void saveSetIntoSet(FlashcardSet flashcardset) {
        int count = flashcardset.getFlashcards().size();
        if (enoughBlankFlashcard(count)) {
            FlashcardSet newSet = new FlashcardSet(flashcardset);
            newSet.setAuthor(Utility.getUser());

            saveFlashcardsIntoNewFlashcardset(newSet, flashcardset);

            Executions.sendRedirect("flashcard.zul?task=edit&id=" + newSet.getId().toString());
        } else {
            Messagebox.show("không đủ flashcards trắng, chuyển sang trang add flashcard trắng. đại loại là thế");
        }
    }

    /**
     * create new flashcard set with name typed and save flashcards are seleted into new flashcard set chkFlashcards:
     * list checkbox
     */
    @Deprecated
    public void createSetAndAddFlashcardsIntoSet(List<Checkbox> chkFlashcards, FlashcardSet flashcardset, String name_set) {

        FlashcardSetService fsSrv = Repository.getInstance().getFlashcardSetService();
        if (name_set.length() > 0) {
            int count = fsSrv.countChecked(chkFlashcards);
            if (count > 0) {
                if (fsSrv.enoughBlankFlashcard(count)) {
                    FlashcardSet newSet = new FlashcardSet();
                    newSet.setName(name_set);
                    newSet.setAuthor(Utility.getUser());

                    for (Checkbox chk : chkFlashcards) {
                        if (chk.isChecked()) {
                            Flashcard insertFlashcard = new Flashcard(flashcardset.getFlashcardById((Long) chk.getValue()));
                            insertFlashcard.setOriflashcardset(flashcardset);
                            newSet.getFlashcards().add(insertFlashcard);
                        }
                    }
                    fsSrv.updateBlankFlashcards(count);
                    fsSrv.updateTotalFlashcards(newSet);
                    fsSrv.save(newSet);
                    Executions.sendRedirect("/flashcard.zul?task=edit&id=" + newSet.getId().toString());
                } else {
                    Clients.evalJavaScript("showNotice('','Không đủ flashcard trắng');");
                }
            } else {
                Clients.evalJavaScript("showNotice('','Vui lòng chọn flashcard trước khi thêm vào lits từ');");
            }
        } else {
            Clients.evalJavaScript("showNotice('','Vui lòng nhập tên flashcard set');");
        }
    }

    /**
     * Do copy flashcards to a new flashcardset
     */
    public void copyFlashcards(FlashcardSet fromSet, List<Long> ids, FlashcardSet toSet) {
        for (Long id : ids) {
            Flashcard fc = fromSet.getFlashcardById(id);
            if (fc == null) {
                continue;
            }
            toSet.addFlashcard(fc.clone());
        }
    }

    /**
     * Do copy flashcards to a new flashcardset
     */
    public void copyFlashcards(FlashcardSet fromSet, FlashcardSet toSet) {
        for (Flashcard fc : fromSet.getFlashcards()) {
            toSet.addFlashcard(fc.clone());
        }
    }

    /**
     * save flashcards are selected into flashcard set
     * 
     * @param chkFlashcards
     *            : list checkbox
     * @param flashcardset
     *            : original flashcardset(current flashcardset)
     * @param setSelected
     *            : flashcardset is selected
     */
    public void saveFlashcardsIntoSet(List<Checkbox> chkFlashcards, FlashcardSet flashcardset, FlashcardSet setSelected) {
        FlashcardSetService fsSrv = Repository.getInstance().getFlashcardSetService();
        int count = fsSrv.countChecked(chkFlashcards);
        if (count > 0) {
            if (fsSrv.enoughBlankFlashcard(count)) {
                if (setSelected == null) {
                    Clients.evalJavaScript("showNotice('','Vui lòng chọn một flashcard set!')");
                    return;
                }
                String flashcardsNoEdit = "";
                FlashcardService fSrv = Repository.getInstance().getFlashcardService();
                for (Checkbox chk : chkFlashcards) {
                    if (chk.isChecked()) {
                        Flashcard insertFlashcard = new Flashcard((Flashcard) chk.getValue());

                        if (fSrv.isExistTerm(insertFlashcard.getTerm(), setSelected.getId())) {
                            count--;
                            flashcardsNoEdit += insertFlashcard.getTerm() + ", ";
                        } else {
                            insertFlashcard.setOriflashcardset(flashcardset);
                            setSelected.getFlashcards().add(insertFlashcard);
                        }
                    }
                }

                fsSrv.updateBlankFlashcards(count);

                fsSrv.updateTotalFlashcards(setSelected);
                fsSrv.save(setSelected);
                String notify = "";
                if (flashcardsNoEdit.length() > 0) {
                    notify = flashcardsNoEdit.substring(0, flashcardsNoEdit.length() - 2);
                }
                String encodeNotify = Executions.getCurrent().encodeURL(notify);

                if (count > 0) {
                    Executions.getCurrent().sendRedirect(
                            "/flashcard.zul?task=edit&id=" + setSelected.getId().toString() + "&notify=" + encodeNotify, "_blank");
                } else {
                    Clients.evalJavaScript("$.showNotice('','Một số flashcard trùng term với flashcard set bạn chọn: " + encodeNotify
                            + "');");
                }
            } else {
                Messagebox.show("redirect to add white flashcards");
            }
        } else {
            Clients.evalJavaScript("showNotice('','Vui lòng chọn flashcard trước khi thêm vào list từ');");
        }
    }

    /**
     * Return the number of know cards
     * 
     * @param flashcardset
     * @return
     */
    public int getNumOfKnowCards(FlashcardSet flashcardset) {
        int result = 0;
        List<Flashcard> lst = flashcardset.getFlashcards();
        for (Flashcard fc : lst) {
            if (fc != null && fc.getTestStatus() == Flashcard.TESTSTATUS_RIGHT) {
                result++;
            }
        }
        return result;
    }

    /**
     * Return the number of unknow cards
     * 
     * @param flashcardset
     * @return
     */
    public int getNumOfUnknowCards(FlashcardSet flashcardset) {
        int result = 0;
        List<Flashcard> lst = flashcardset.getFlashcards();
        for (Flashcard fc : lst) {
            if (fc != null && fc.getTestStatus() == Flashcard.TESTSTATUS_FAIL) {
                result++;
            }
        }
        return result;
    }

    /**
     * Return the number of new cards
     * 
     * @param flashcardset
     * @return
     */
    public int getNumOfNewCards(FlashcardSet flashcardset) {
        int result = 0;
        List<Flashcard> lst = flashcardset.getFlashcards();
        for (Flashcard fc : lst) {
            if (fc != null && fc.getTestStatus() == Flashcard.TESTSTATUS_NOTYET) {
                result++;
            }
        }
        return result;
    }

    /**
     * The number of not learn at level 0
     * 
     * @param flashcardset
     * @return
     */
    public int getNumOfFreshCards(FlashcardSet flashcardset) {
        int result = 0;
        List<Flashcard> lst = flashcardset.getFlashcards();
        for (Flashcard fc : lst) {
            if (fc != null && fc.getLearnLevel() == Flashcard.LEARNLEVEL_NEW) {
                result++;
            }
        }
        return result;
    }

    /**
     * Load a comment by id
     * 
     * @param id
     * @return
     */
    public FCSComment loadFCSComment(String id) {
        BasicDAO<FCSComment, ObjectId> srv = getFCSCommentDAO();
        return srv.get(new ObjectId(id));
    }

    /**
     * Save a comment to database
     * 
     * @param comment
     */
    public void saveFCSComment(FCSComment comment) {
        BasicDAO<FCSComment, ObjectId> srv = getFCSCommentDAO();
        srv.save(comment);
    }

    /**
     * Delete a comment
     * 
     * @param comment
     */
    public void deleteFCSComment(FCSComment comment, FlashcardSet flashcardset) {
        if (Utility.getUser().equals(flashcardset.getAuthor())) {
            BasicDAO<FCSComment, ObjectId> srv = getFCSCommentDAO();
            srv.delete(comment);
        } else {
            Utility.displayAccessDenied(false);
        }
    }

    /**
     * Get list of comments of flashcardset
     * 
     * @param flashcardsetid
     * @return
     */
    public List<FCSComment> getCommentsOfFlashcardset(Long flashcardsetid, int page) {
        int limit = FlashcardSet.LIMIT_COMMENTS;
        int skip = (limit * (page - 1));

        BasicDAO<FCSComment, ObjectId> srv = getFCSCommentDAO();
        com.google.code.morphia.query.Query<FCSComment> query = srv.createQuery();
        query.field("fcsid").equal(flashcardsetid);
        query.order("-created");

        query.skip(skip).limit(limit);
        return query.asList();
    }

    /**
     * get pages
     */
    public long countPage(Long flashcardsetid) {

        BasicDAO<FCSComment, ObjectId> srv = getFCSCommentDAO();
        com.google.code.morphia.query.Query<FCSComment> query = srv.createQuery();
        query.field("fcsid").equal(flashcardsetid);

        return query.countAll();
    }

    /**
     * find FCSComment by ID
     * 
     * @param flashcardsetid
     * @return
     */
    public FCSComment findCommentById(Long fcsCommentId) {
        BasicDAO<FCSComment, ObjectId> srv = getFCSCommentDAO();
        return srv.get(new ObjectId(String.valueOf(fcsCommentId)));
    }

    public FlashcardSet findFlashcardSetByName(String name) {
        com.lt.flashcard.model.service.BasicDAO<FlashcardSet> dao = new com.lt.flashcard.model.service.BasicDAO<FlashcardSet>(
                FlashcardSet.class);
        Criteria crit = dao.createCriteria();
        crit.add(Restrictions.eq("name", name));
        FlashcardSet fc = (FlashcardSet) crit.uniqueResult();
        return fc;
    }

    public List<FlashcardSet> findAllFlashcardSets() {
        com.lt.flashcard.model.service.BasicDAO<FlashcardSet> dao = new com.lt.flashcard.model.service.BasicDAO<FlashcardSet>(
                FlashcardSet.class);
        Criteria crit = dao.createCriteria();
        crit.addOrder(Order.asc("name"));
        return crit.list();
    }

    /**
     * Get all falshcard set of admin
     * 
     * @return
     */
    public List<FlashcardSet> getAllAdminFlashcardSets() {
        com.lt.flashcard.model.service.BasicDAO<FlashcardSet> dao = new com.lt.flashcard.model.service.BasicDAO<FlashcardSet>(
                FlashcardSet.class);
        Criteria crit = dao.createCriteria();
        crit.add(Restrictions.eq("admin", FlashcardSet.ADMIN_YES));
        crit.addOrder(Order.asc("name"));
        return crit.list();
    }

    public List<FlashcardSet> getAllFlashcardSets(Long authorId, int size, int page, String sort, String order, List<Long> tagIds,
            String keyword) {
        Criteria crit = getCurrentSession().createCriteria(FlashcardSet.class);
        if (authorId != null) {
            crit.add(Restrictions.eq("author.id", authorId));
        }
        if (!tagIds.isEmpty()) {
            crit.createAlias("tags", "t", Criteria.INNER_JOIN);
            crit.add(Restrictions.in("t.id", tagIds));
        }
        if (StringUtils.isNotBlank(keyword)) {
            crit.add(Restrictions.or(Restrictions.ilike("name", "%" + keyword + "%"),
                    Restrictions.ilike("description", "%" + keyword + "%")));
        }
        crit.addOrder("asc".equals(order) ? Order.asc(sort) : Order.desc(sort));
        crit.setMaxResults(size);
        crit.setFirstResult(page * size);
        return crit.list();
    }

    /**
     * Check if flashcardset is of owner
     * 
     * @param owner
     * @param flashcardset
     * @return
     */
    public boolean checkOwnFlashcardSet(User owner, FlashcardSet flashcardset) {
        return owner != null && owner.getId() != null && owner.getId().equals(flashcardset.getAuthor().getId());
    }

    @Override
    /**
     * Delete a flashcardset
     */
    public void delete(FlashcardSet entity) {
        if (entity == null) {
            return;
        }
        if (log.isInfoEnabled()) {
            log.info("Delete flashcardset " + entity.getId());
        }
        if (entity.getAdmin() == FlashcardSet.ADMIN_NO) {
            Repository repo = Repository.getInstance();
            /*
             * for (Flashcard fc : entity.getFlashcards()) { if (fc != null) { repo.deleteBinaryData(fc.getImage()); } }
             * repo.deleteBinaryData(entity.getImage());
             */
            // TODO delete sound
            // delete original flashcardset
            String sql = "Update Flashcard set oriflashcardset = null where oriflashcardset = :oldset";
            int af = getCurrentSession().createQuery(sql).setEntity("oldset", entity).executeUpdate();
            if (log.isInfoEnabled()) {
                log.info("Batch delete oriflashcardset of " + entity.getId());
            }
            // TODO must optimize the delete from multi delete to one delete query
            super.delete(entity);
        } else if (log.isInfoEnabled()) {
            log.info("Can't delete fcs " + entity.getId() + " with admin status " + entity.getAdmin());
        }
    }

    /**
     * Do filter flashcards by name and author name
     * 
     * @param name
     * @param author
     * @return
     */
    public List<FlashcardSet> fiterFlashcardSets(String name, String author, int offset, int pagesize) {
        com.lt.flashcard.model.service.BasicDAO<FlashcardSet> dao = new com.lt.flashcard.model.service.BasicDAO<FlashcardSet>(
                FlashcardSet.class);
        Criteria crit = dao.createCriteria();
        if (!Utility.isEmptyString(name)) {
            crit.add(Restrictions.like("name", name, MatchMode.ANYWHERE));
        }
        if (!Utility.isEmptyString(author)) {
            crit.createAlias("author", "a");
            crit.add(Restrictions.like("a.username", author, MatchMode.START));
        }
        crit.setMaxResults(pagesize);
        crit.setFirstResult(offset);
        crit.addOrder(Order.asc("name"));
        return crit.list();
    }

    /**
     * Count the number of result
     * 
     * @param name
     * @param author
     * @param offset
     * @param pagesize
     * @return
     */
    public int countFilterFlashcardSets(String name, String author) {
        com.lt.flashcard.model.service.BasicDAO<FlashcardSet> dao = new com.lt.flashcard.model.service.BasicDAO<FlashcardSet>(
                FlashcardSet.class);
        Criteria crit = dao.createCriteria();
        if (!Utility.isEmptyString(name)) {
            crit.add(Restrictions.like("name", name, MatchMode.ANYWHERE));
        }
        if (!Utility.isEmptyString(author)) {
            crit.createAlias("author", "a");
            crit.add(Restrictions.like("a.username", author, MatchMode.START));
        }
        crit.setProjection(Projections.rowCount());
        return (Integer) crit.uniqueResult();
    }

    /**
     * Check to see if dupplicate flashcardset name
     * 
     * @param name
     * @param targetId
     *            The id that will be excluded
     * @param isAdmin
     *            True: the targetid is of admin, so can have same name with other bought flashcard
     * @return
     */
    public boolean isDupplicateFlashcardSetName(String name, Long targetId, boolean isAdmin) {
        if (Utility.isEmptyString(name)) {
            return true;
        }

        org.hibernate.Session srv = getCurrentSession();
        Query query;
        if (targetId != null) {
            String sql = "select count(*) from FlashcardSet fcs where name = :name and id <> :targetid "
                    + (isAdmin ? "and admin = :admin" : "");
            query = srv.createQuery(sql);
            query.setParameter("targetid", targetId);
        } else {
            query = srv.createQuery("select count(*) from FlashcardSet fcs where name = :name " + (isAdmin ? "and admin = :admin" : ""));
        }
        if (isAdmin) {
            query.setParameter("admin", FlashcardSet.ADMIN_YES);
        }
        query.setParameter("name", name);
        Iterator count = query.iterate();
        return ((Long) count.next()) > 0l;
    }

    /**
     * Count the number of sold products of a specific product
     * 
     * @param productid
     * @return
     */
    public int countNumberOfSoldProducts(Long productid) {
        Criteria crit = createCriteria();
        crit.add(Restrictions.eq("admin", FlashcardSet.ADMIN_BUY));
        crit.add(Restrictions.eq("originalflashcardsetCache", String.valueOf(productid)));
        crit.setProjection(Projections.rowCount());
        return (Integer) crit.uniqueResult();
    }
}
